///************************************************************************
/// <copyrigth>2018-2019 Corporation.All Rights Reserved</copyrigth>
/// <author>tangyingzhong</author>
/// <contact>tangyz114987@outlook.com</contact>
/// <version>v1.0.0</version>
/// <describe>
/// It can be used to operate all the plugins
///</describe>
/// <date>2019/11/6</date>
///***********************************************************************
#ifndef PLUGINPTR_H
#define PLUGINPTR_H

#include <string>
#include <memory>
#include "PluginLoader.h"
#include "Model/IFramework.h"

template <class T>
class PluginPtr
{
public:
	typedef T ValueType;
	typedef ValueType* Pointer;
	typedef ValueType& Reference;

public:
	// Construct the plugin pointer
	PluginPtr() :
		m_pPointer(NULL),
		m_strPluginName(""),
		m_iMajorVersion(0),
		m_iMinorVersion(0),
		m_iModifyVersion(0),
		m_pFramework(NULL),
		m_bDisposed(false)
	{
		Initialize();
	}

	// Construct the plugin pointer
	PluginPtr(std::string strPluginName,
		int iMajorVersion,
		int iMinorVersion,
		int iModifyVersion) :
		m_pPointer(NULL),
		m_strPluginName(strPluginName),
		m_iMajorVersion(iMajorVersion),
		m_iMinorVersion(iMinorVersion),
		m_iModifyVersion(iModifyVersion),
		m_pFramework(NULL),
		m_bDisposed(false)
	{
		Initialize();
	}

	// Destory the plugin pointer
	~PluginPtr()
	{
		if (!GetDisposed())
		{
			SetDisposed(true);

			Destory();
		}
	}

	// Forbid the plugin pointer's copy
	PluginPtr(const PluginPtr& other)
	{
		m_pPointer = other.m_pPointer;

		m_strPluginName = other.m_strPluginName;

		m_iMajorVersion = other.m_iMajorVersion;

		m_iMinorVersion = other.m_iMinorVersion;

		m_iModifyVersion = other.m_iModifyVersion;

		m_bDisposed = other.m_bDisposed;
	}

	// Forbid the pointer's assigment
	PluginPtr& operator=(const PluginPtr& other)
	{
		if (this != &other)
		{
			m_pPointer = other.m_pPointer;

			m_strPluginName = other.m_strPluginName;

			m_iMajorVersion = other.m_iMajorVersion;

			m_iMinorVersion = other.m_iMinorVersion;

			m_iModifyVersion = other.m_iModifyVersion;

			m_bDisposed = other.m_bDisposed;
		}
		
		return *this;
	}

public:
	// Load plugin
	bool LoadPlugin(std::string strPluginName,
		int iMajorVersion,
		int iMinorVersion,
		int iModifyVersion)
	{
		if (strPluginName.empty())
		{
			return false;
		}

		if (iMajorVersion<0)
		{
			return false;
		}

		if (iMinorVersion<0)
		{
			return false;
		}

		if (iModifyVersion<0)
		{
			return false;
		}

		SetPluginName(strPluginName);

		SetMajorVersion(iMajorVersion);

		SetMinorVersion(iMinorVersion);

		SetModifyVersion(iModifyVersion);

		// Get plugin from the micro core
		if (GetFramework())
		{
			Pointer pInterface = NULL;

			if (strPluginName == FRAMEWORK_NAME)
			{
				pInterface = static_cast<Pointer>(static_cast<IPlugin*>(GetFramework()));
			}
			else
			{
				IPlugin* pPlugin = GetFramework()->GetPlugin(strPluginName, iMajorVersion, iMinorVersion, iModifyVersion);

				if (pPlugin == NULL)
				{
					return false;
				}

				pInterface = static_cast<Pointer>(pPlugin);
			}

			SetPointer(pInterface);

			return true;
		}

		return false;
	}

	// Is valid
	bool IsValid()
	{
		if (GetPointer() == NULL)
		{
			return false;
		}

		return true;
	}

	// Get pointer data
	Pointer Data()
	{
		if (!IsValid())
		{
			return NULL;
		}

		return GetPointer();
	}

	// Support ->
	Pointer operator->()
	{
		return Data();
	}

	// Support *
	Reference operator*()
	{
		return *(Data());
	}

private:
	// Initialize the App
	void Initialize()
	{
		// Create framework
		CreateFramework();

		// Load plugin
		LoadPlugin(GetPluginName(),
			GetMajorVersion(),
			GetMinorVersion(),
			GetModifyVersion());
	}

	// Destory the App
	void Destory()
	{
		if (!GetDisposed())
		{
			SetDisposed(true);

			// Destory the framework
			DestoryFramework();
		}
	}

	// Create the framework
	void CreateFramework()
	{
		m_Loader.Load(FRAMEWORK_NAME);

		IFramework* pFramework = m_Loader.Data();

		SetFramework(pFramework);
	}

	// Destory the framework
	void DestoryFramework() {	}

private:
	// Get the Pointer
	inline Pointer GetPointer() const
	{
		return m_pPointer;
	}

	// Set the Pointer
	inline void SetPointer(Pointer pPointer)
	{
		m_pPointer = pPointer;
	}

	// Get the PluginName
	inline std::string GetPluginName() const
	{
		return m_strPluginName;
	}

	// Set the PluginName
	inline void SetPluginName(std::string strPluginName)
	{
		m_strPluginName = strPluginName;
	}

	// Get the MajorVersion
	inline int GetMajorVersion() const
	{
		return m_iMajorVersion;
	}

	// Set the MajorVersion
	inline void SetMajorVersion(int iMajorVersion)
	{
		m_iMajorVersion = iMajorVersion;
	}

	// Get the MinorVersion
	inline int GetMinorVersion() const
	{
		return m_iMinorVersion;
	}

	// Set the MinorVersion
	inline void SetMinorVersion(int iMinorVersion)
	{
		m_iMinorVersion = iMinorVersion;
	}

	// Get the ModifyVersion
	inline int GetModifyVersion() const
	{
		return m_iModifyVersion;
	}

	// Set the ModifyVersion
	inline void SetModifyVersion(int iModifyVersion)
	{
		m_iModifyVersion = iModifyVersion;
	}

	// Get Framework
	inline IFramework* GetFramework() const
	{
		return m_pFramework;
	}

	// Set Framework
	inline void SetFramework(IFramework* pFramework)
	{
		m_pFramework = pFramework;
	}

	// Get the Disposed
	inline bool GetDisposed() const
	{
		return m_bDisposed;
	}

	// Set the Disposed
	inline void SetDisposed(bool bDisposed)
	{
		m_bDisposed = bDisposed;
	}

private:
	// Object pointer
	Pointer m_pPointer;

	// Plugin name
	std::string m_strPluginName;

	// Plugin's major version
	int m_iMajorVersion;
	
	// Plugin's minor version
	int m_iMinorVersion;
	
	// Plugin's modify version
	int m_iModifyVersion;

	// Framework
	IFramework* m_pFramework;

	// Framework loader
	PluginLoader<IFramework> m_Loader;
	
	// Disposed status
	bool m_bDisposed;
};

#endif // PLUGINPTR_H